package org.fnppl.opensdx.keyserverfe.client; import java.util.Date; import java.util.HashMap; import java.util.Vector; import org.fnppl.opensdx.keyserverfe.shared.FieldVerifier; import org.fnppl.opensdx.keyserverfe.shared.KeyConnection; import org.fnppl.opensdx.keyserverfe.shared.KeyInfo; import org.fnppl.opensdx.keyserverfe.shared.NodeState; import org.fnppl.opensdx.keyserverfe.shared.User; import com.google.gwt.canvas.client.Canvas; import com.google.gwt.canvas.dom.client.Context2d; import com.google.gwt.canvas.dom.client.CssColor; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.dom.client.MouseMoveEvent; import com.google.gwt.event.dom.client.MouseMoveHandler; import com.google.gwt.event.dom.client.MouseUpEvent; import com.google.gwt.event.dom.client.MouseUpHandler; import com.google.gwt.i18n.client.DateTimeFormat; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.AbsolutePanel; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.DialogBox; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.PasswordTextBox; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; public class OSDXKeyserverFE implements EntryPoint { private static final String SERVER_ERROR = "An error occurred while " + "attempting to contact the server. Please check your network " + "connection and try again."; private final KeyserverServiceAsync keyserverService = GWT.create(KeyserverService.class); private static int UI_LOGIN = 0; private static int UI_USER = 1; private int uiState = UI_LOGIN; private User user = null; private VerticalPanel mainPanel = null; private FlexTable tableKeys = null; private Canvas graph = null; private Context2d graphContext = null; private Canvas graphOverlay = null; private Context2d graphOverlayContext = null; private int visibilityLevel = 1; private KeyDetailsPopupPanel keyDetails = new KeyDetailsPopupPanel(); private Label userErrorLabel = null; private int mouseX = 0; private int mouseY = 0; // private int mouseDownX = 0; // private int mouseDownY = 0; // private int mouseUpX = 0; // private int mouseUpY = 0; private boolean mouseDown = false; private boolean shiftDown = false; private KeyInfo selectedNode = null; private Vector<int[]> selectedFrom = new Vector<int[]>(); private Vector<int[]> selectedTo = new Vector<int[]>(); private Vector<CheckBox> checks = null; private ClickHandler checkBoxHandler = null; private CheckBox cbShowDetails = null; private HashMap<String, KeyInfo> keyid_key = new HashMap<String, KeyInfo>(); private HashMap<String, Vector<KeyInfo>> keyid_connect_in = new HashMap<String, Vector<KeyInfo>>(); private HashMap<String, Vector<KeyInfo>> keyid_connect_out = new HashMap<String, Vector<KeyInfo>>(); private HashMap<String, Integer> connect_count = new HashMap<String, Integer>(); private DateTimeFormat datetimeformat = DateTimeFormat.getFormat("yyyy-MM-dd HH:mm:ss zzz"); private int w = 850; private int h = 600; private double fullCircle = Math.toRadians(360); private double phi = Math.toRadians(15); private double deg180 = Math.toRadians(180); private int radiusNode = 16; private int radiusCircle = 12; private int barb = 20; private static CssColor selectColor = CssColor.make(250,0,0); private static CssColor selectColor2 = CssColor.make(225,111, 27); private static CssColor validColor = CssColor.make(28, 185, 13); private static CssColor notValidColor = CssColor.make(250,0,0); private static CssColor[] nodeColors = new CssColor[] {CssColor.make(0,0,0),CssColor.make(44,95,200), CssColor.make(195,179,2), CssColor.make(173, 114, 198)}; private static CssColor[] nodeTextColor = new CssColor[] {CssColor.make(255,255,255),CssColor.make(200,200,200),CssColor.make(0,0,0),CssColor.make(0,0,0)}; private static CssColor[] edgeColors = new CssColor[7]; static { edgeColors[KeyConnection.TYPE_UNKNOWN] = CssColor.make(0, 0,0); edgeColors[KeyConnection.TYPE_APPROVAL] = CssColor.make(28, 135, 13); edgeColors[KeyConnection.TYPE_APPROVAL_PENDING] = CssColor.make(94, 241, 75); edgeColors[KeyConnection.TYPE_DISAPPROVAL] = CssColor.make(200, 9, 104); edgeColors[KeyConnection.TYPE_REVOCATION] = CssColor.make(116, 27, 71); edgeColors[KeyConnection.TYPE_SUBKEY] = CssColor.make(54, 141, 142); edgeColors[KeyConnection.TYPE_REVOKEKEY] = CssColor.make(173, 114, 198); } public void onModuleLoad() { mainPanel = new VerticalPanel(); RootPanel.get("mainPanel").add(mainPanel); updateUI(UI_LOGIN); } private ClickHandler buKeyLogHandler = null; public void updateUI(int newState) { if (newState == UI_LOGIN) { initLoginView(); } else if (newState == UI_USER) { if (uiState != UI_USER) { initUserView(); updateUserView(); uiState = UI_USER; } else { updateUserView(); } } } private void updateUserView() { updateKeyTable(); layoutNodes(); drawGraph(); } private void updateKeyTable() { //update table Vector<KeyInfo> keys = user.getKeys(); checks = new Vector<CheckBox>(); tableKeys.removeAllRows(); tableKeys.setHTML(0, 0, "no"); tableKeys.setHTML(0, 1, "keyid short"); tableKeys.setHTML(0, 2, "owner"); tableKeys.setHTML(0, 3, "mnemonic"); tableKeys.setHTML(0, 4, "level"); tableKeys.setHTML(0, 5, "usage"); tableKeys.setHTML(0, 6, "status"); tableKeys.setHTML(0, 7, "selected"); tableKeys.setHTML(0, 8, "logs"); for (int i=0;i<keys.size();i++) { try { KeyInfo ki = keys.get(i); CheckBox cb = new CheckBox(); cb.addClickHandler(checkBoxHandler); cb.setStylePrimaryName("myTableChecks"); checks.add(cb); int row = i+1; ki.setNo(row); tableKeys.setHTML(row, 0, ""+row); tableKeys.setHTML(row, 1, ki.getIdShort()); tableKeys.setHTML(row, 2, ki.getOwner()); tableKeys.setHTML(row, 3, ki.getMnemonic()); tableKeys.setHTML(row, 4, ki.getLevel()); tableKeys.setHTML(row, 5, ki.getUsage()); tableKeys.setHTML(row, 6, ki.getStatusText()); tableKeys.setWidget(row, 7, cb); Button kl = new Button("logs"); kl.setTitle(ki.getId()); kl.addClickHandler(buKeyLogHandler); kl.setStylePrimaryName("keylogButton"); tableKeys.setWidget(row, 8, kl); } catch (Exception ex) { ex.printStackTrace(); } } //style tableKeys.getRowFormatter().setStylePrimaryName(0, "myTableHeader"); } private void initUserView() { mainPanel.clear(); initKeyLogHandler(); initCheckBoxHandler(); //init key table tableKeys = new FlexTable(); tableKeys.setTitle("Keys"); ScrollPanel scrollTable = new ScrollPanel(wrapInDiv("keytablePanel", tableKeys)); scrollTable.addStyleName("scrollPanel"); scrollTable.setWidth(w+"px"); scrollTable.setHeight("300px"); //mainPanel.add(wrapInDiv("keytablePanel", scrollTable)); mainPanel.add(scrollTable); //init error label userErrorLabel = new Label(); userErrorLabel.addStyleName("errorLabel"); mainPanel.add(userErrorLabel); initAddKeyPanel(); initSelectionPanel(); initButtonsPanel(); buildCanvas(); layoutNodes(); drawGraph(); } private void initButtonsPanel() { //panel Buttons HorizontalPanel panelButtons = new HorizontalPanel(); cbShowDetails = new CheckBox("show key details"); cbShowDetails.setValue(true); panelButtons.add(cbShowDetails); final Button selectAll = new Button("select all"); selectAll.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { if (checks==null) return; for (CheckBox cb : checks) { cb.setValue(true); } graphOverlayContext.clearRect(0,0,w,h); drawSelected(); } }); panelButtons.add(selectAll); final Button selectNone = new Button("select none"); selectNone.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { if (checks==null) return; for (CheckBox cb : checks) { cb.setValue(false); } graphOverlayContext.clearRect(0,0,w,h); drawSelected(); } }); panelButtons.add(selectNone); mainPanel.add(wrapInDiv("buttonsPanel",panelButtons)); } private void initSelectionPanel() { //panel Selection HorizontalPanel panelSelection = new HorizontalPanel(); final Button buConnectIn = new Button("add incoming keylogs"); buConnectIn.setTitle("Requests alll keylogs and related keys that are incoming to the selected keys."); buConnectIn.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { addKeysAndLogsForSelection(true,false,userErrorLabel); } }); final Button buConnectOut = new Button("add outgoing keylogs"); buConnectOut.setTitle("Requests all keylogs and related keys that are outgoinig from the selected keys."); buConnectOut.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { addKeysAndLogsForSelection(false,true,userErrorLabel); } }); final Button buConnectInOut = new Button("add incoming/outgoing keylogs"); buConnectInOut.setTitle("Requests all keylogs and related keys that are incoming to or outgoinig from the selected keys."); buConnectInOut.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { addKeysAndLogsForSelection(true,true,userErrorLabel); } }); final Button buRemove = new Button("remove keys and logs"); buRemove.setTitle("Removes all selected keys and related keylogs."); buRemove.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { removeSelectedKeys(userErrorLabel); } }); panelSelection.add(new Label("Selection:")); panelSelection.add(buConnectIn); panelSelection.add(buConnectOut); panelSelection.add(buConnectInOut); panelSelection.add(buRemove); mainPanel.add(wrapInDiv("selectionsPanel",panelSelection)); } private void initAddKeyPanel() { HorizontalPanel panelAdd = new HorizontalPanel(); panelAdd.addStyleName("hPanel"); final TextBox txKeyid = new TextBox(); txKeyid.setText("3C:02:09:27:0B:DD:99:3F:44:DD:D0:27:1A:5C:38:CE:AF:04:F5:4E@localhost"); txKeyid.setWidth("600px"); final Button buAdd = new Button("add"); buAdd.addStyleName("sendButton"); buAdd.setWidth("80px"); buAdd.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { String addKeyid = txKeyid.getText(); if (!FieldVerifier.isValidKeyId(addKeyid)) { userErrorLabel.setVisible(true); userErrorLabel.setText("Please enter a valid keyid"); Timer t = new Timer() { public void run() { userErrorLabel.setVisible(false); userErrorLabel.setText(""); } }; t.schedule(4000); return; } Vector<String> keyids = new Vector<String>(); keyids.add(addKeyid); boolean inLogs = true; boolean outLogs = true; keyserverService.updateKeyInfoAndLogs(user.getName(), getCurrentNodeStates(), keyids, inLogs, outLogs, new AsyncCallback<User>() { public void onFailure(Throwable caught) { userErrorLabel.setVisible(true); userErrorLabel.setText(SERVER_ERROR); Timer t = new Timer() { public void run() { userErrorLabel.setVisible(false); userErrorLabel.setText(""); } }; t.schedule(4000); } public void onSuccess(User result) { if (result!=null) { for (KeyInfo ki : result.getKeys()) { user.addKey(ki); } for (KeyConnection kc : result.getConnections()) { user.addConnection(kc); } updateUI(UI_USER); } } }); } }); panelAdd.add(new Label("Key id:")); panelAdd.add(txKeyid); panelAdd.add(buAdd); mainPanel.add(wrapInDiv("addPanel",panelAdd)); } private void initCheckBoxHandler() { if (checkBoxHandler == null) { checkBoxHandler = new ClickHandler() { public void onClick(ClickEvent event) { graphOverlayContext.clearRect(0,0,w,h); drawSelected(); } }; } } private void initKeyLogHandler() { //build user ui if (buKeyLogHandler == null) { buKeyLogHandler = new ClickHandler() { public void onClick(ClickEvent event) { String keyid = "unknown"; Object srcObj= event.getSource(); if (srcObj instanceof Button) { Button b = (Button)srcObj; keyid = b.getTitle(); if (keyid!=null && keyid.length()>0) { Vector<KeyConnection> logs = user.getLogs(keyid); if (logs.size()==0) { String msg = "No KeyLogs found."; DialogBox box = buildMessageBox("Keylogs for Key: "+keyid, msg); box.setPopupPosition(event.getClientX()-400, event.getClientY()+10); box.show(); } else { FlexTable tableLogs = new FlexTable(); tableLogs.setTitle("KeyLogs"); tableLogs.setHTML(0, 0, "from"); tableLogs.setHTML(0, 1, "to"); tableLogs.setHTML(0, 2, "type"); tableLogs.setHTML(0, 3, "date"); for (int i=0;i<logs.size();i++) { KeyConnection kc = logs.get(i); int row = i+1; KeyInfo kiFrom = keyid_key.get(kc.getFromId()); KeyInfo kiTo = keyid_key.get(kc.getToId()); if (kiFrom==null) { tableLogs.setHTML(row, 0, kc.getFromId()); } else { tableLogs.setHTML(row, 0, kiFrom.getIdShort()+"<br/>"+kiFrom.getOwner()); } if (kiTo==null) { tableLogs.setHTML(row, 1, kc.getToId()); } else { tableLogs.setHTML(row, 1, kiTo.getIdShort()+"<br/>"+kiTo.getOwner()); } tableLogs.setHTML(row, 2, kc.getTypeText()); tableLogs.setHTML(row, 3, datetimeformat.format(new Date(kc.getDate()))); } //style tableLogs.getRowFormatter().setStylePrimaryName(0, "myTableHeader"); ScrollPanel scroll = new ScrollPanel(wrapInDiv("keylogPanel",tableLogs)); scroll.addStyleName("scrollPanel"); DialogBox box = buildBox("Keylogs for Key: "+keyid, scroll); box.setPopupPosition(event.getClientX()-400, event.getClientY()+10); box.show(); } } } } }; } } private void initLoginView() { mainPanel.clear(); mainPanel.setSize("700px", "150px"); //panel Login final VerticalPanel panelLogin = new VerticalPanel(); final Button buLogin = new Button("Login"); buLogin.addStyleName("sendButton"); buLogin.setWidth("150px"); final TextBox txUser = new TextBox(); txUser.setText("debug@it-is-awesome.de"); txUser.setWidth("300px"); final PasswordTextBox txPW = new PasswordTextBox(); txPW.setText(""); txPW.setWidth("300px"); final Label errorLabel1 = new Label(); errorLabel1.addStyleName("errorLabel"); FlexTable tabL = new FlexTable(); tabL.setTitle("Start anonymouse session"); tabL.setWidget(0,0,new Label("Email:")); tabL.setWidget(0,1,txUser); tabL.setWidget(1,0,new Label("Password:")); tabL.setWidget(1,1,txPW); tabL.setWidget(2,1,buLogin); panelLogin.add(new HTML("<h3>Login</h3>")); panelLogin.add(tabL); panelLogin.add(errorLabel1); panelLogin.addStyleName("innerPanel"); class LoginHandler implements ClickHandler, KeyUpHandler { public void onClick(ClickEvent event) { login(); } public void onKeyUp(KeyUpEvent event) { if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { login(); } } private void login() { //errorLabel1.setText("Login not implemented"); errorLabel1.setText(""); String username = txUser.getText(); String password = txPW.getText(); if (username.length()==0) { errorLabel1.setText("Please enter a correct email address"); return; } if (password.length()==0) { errorLabel1.setText("Please enter a password"); return; } buLogin.setEnabled(false); keyserverService.login(username, password, new AsyncCallback<User>() { public void onFailure(Throwable caught) { errorLabel1.setText(SERVER_ERROR); buLogin.setEnabled(true); } public void onSuccess(User result) { if (result == null) { errorLabel1.setText("Wrong email or password"); buLogin.setEnabled(true); } else { user = result; updateUI(UI_USER); } } } ); } } LoginHandler lHandler = new LoginHandler(); buLogin.addClickHandler(lHandler); txPW.addKeyUpHandler(lHandler); //panel Anonymous final VerticalPanel panelAnonymous = new VerticalPanel(); final Button buStart = new Button("Start"); buStart.addStyleName("sendButton"); buStart.setWidth("100px"); final TextBox txKeyid = new TextBox(); txKeyid.setText("81:9A:BC:86:49:2B:0A:17:82:52:2A:D2:34:1E:B0:34:22:04:52:38@it-is-awesome.de"); txKeyid.setWidth("600px"); final Label errorLabel2 = new Label(); errorLabel2.addStyleName("errorLabel"); FlexTable tab = new FlexTable(); tab.setTitle("Start anonymous session"); tab.setWidget(0,0,new Label("Key ID:")); tab.setWidget(0,1,txKeyid); tab.setWidget(1,1,buStart); panelAnonymous.add(new HTML("<h3>Start anonymous session</h3>")); panelAnonymous.add(tab); panelAnonymous.add(errorLabel2); panelAnonymous.addStyleName("innerPanel"); mainPanel.add(wrapInDiv("loginPanel", panelLogin)); mainPanel.add(wrapInDiv("anonymousPanel", panelAnonymous)); txKeyid.setFocus(true); txKeyid.selectAll(); class AnonymousHandler implements ClickHandler, KeyUpHandler { public void onClick(ClickEvent event) { loginAnonymous(); } public void onKeyUp(KeyUpEvent event) { if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { loginAnonymous(); } } private void loginAnonymous() { buStart.setEnabled(false); errorLabel2.setText(""); String textToServer = txKeyid.getText(); if (!FieldVerifier.isValidKeyId(textToServer)) { errorLabel2.setText("Please enter a valid keyid"); return; } keyserverService.loginAnonymous(textToServer, new AsyncCallback<User>() { public void onFailure(Throwable caught) { errorLabel2.setText(SERVER_ERROR); } public void onSuccess(User result) { user = result; updateUI(UI_USER); } } ); } } AnonymousHandler aHandler = new AnonymousHandler(); buStart.addClickHandler(aHandler); txKeyid.addKeyUpHandler(aHandler); } private HTMLPanel wrapInDiv(String id, Widget w) { HTMLPanel pan = new HTMLPanel(""); pan.getElement().setId(id); pan.add(w); return pan; } private void removeSelectedKeys(final Label errorLabel) { Vector<String> keyids = getSelectedKeyIds(); for (String keyid : keyids) { user.removeKey(keyid); } updateUI(UI_USER); // 13:1E:17:1A:A6:55:87:FA:20:0B:20:43:F7:79:DE:0C:09:6D:89:C8@localhost } private void addKeysAndLogsForSelection(final boolean inLogs, final boolean outLogs, final Label errorLabel) { final Vector<String> keyids = getSelectedKeyIds(); if (keyids.size()==0) { errorLabel.setVisible(true); errorLabel.setText("Please select a key first."); Timer t = new Timer() { public void run() { errorLabel.setVisible(false); errorLabel.setText(""); } }; t.schedule(3000); return; } keyserverService.updateKeyInfoAndLogs(user.getName(), getCurrentNodeStates(), keyids, inLogs, outLogs, new AsyncCallback<User>() { public void onFailure(Throwable caught) { errorLabel.setVisible(true); errorLabel.setText(SERVER_ERROR); Timer t = new Timer() { public void run() { errorLabel.setVisible(false); errorLabel.setText(""); } }; t.schedule(4000); } public void onSuccess(User result) { if (result!=null) { for (KeyInfo ki : result.getKeys()) { user.addKey(ki); } for (KeyConnection kc : result.getConnections()) { user.addConnection(kc); } //update keys states of selected keys for (int i=0;i<keyids.size();i++) { KeyInfo ki = user.getKey(keyids.get(i)); if (ki!=null) { ki.setIncomingLogs(inLogs); ki.setOutgoingLogs(outLogs); } } updateUI(UI_USER); } } }); } private Vector<String> getSelectedKeyIds() { Vector<String> sel = new Vector<String>(); for (int i=0;i<checks.size();i++) { if (checks.get(i).getValue()) { sel.add(user.getKeys().get(i).getId()); } } return sel; } private DialogBox buildMessageBox(String title, String msgHtml) { final DialogBox dialogBox = new DialogBox(); dialogBox.setText(title); dialogBox.setAnimationEnabled(true); final Button buClose = new Button("Close"); final HTML msg = new HTML(msgHtml); VerticalPanel vPanel = new VerticalPanel(); vPanel.add(msg); vPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT); vPanel.add(buClose); dialogBox.setWidget(vPanel); buClose.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { dialogBox.hide(); } }); return dialogBox; } private DialogBox buildBox(String title, Widget widget) { final DialogBox dialogBox = new DialogBox(); dialogBox.setText(title); dialogBox.setAnimationEnabled(true); final Button buClose = new Button("Close"); VerticalPanel vPanel = new VerticalPanel(); vPanel.add(widget); vPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT); vPanel.add(buClose); dialogBox.setWidget(vPanel); buClose.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { dialogBox.hide(); } }); return dialogBox; } public void buildCanvas() { graph = Canvas.createIfSupported(); if (graph == null) { RootPanel.get().insert(new Label("Your browser does NOT support HTML5 Canvas!!"), 0); return; } //graph.setStyleName("mainCanvas"); graph.setWidth(w+"px"); graph.setCoordinateSpaceWidth(w); graph.setHeight(h+"px"); graph.setCoordinateSpaceHeight(h); graphContext = graph.getContext2d(); graphOverlay = Canvas.createIfSupported(); graphOverlay.setWidth(w+"px"); graphOverlay.setCoordinateSpaceWidth(w); graphOverlay.setHeight(h+"px"); graphOverlay.setCoordinateSpaceHeight(h); graphOverlayContext = graphOverlay.getContext2d(); graphOverlayContext.clearRect(0,0,w,h); graphOverlayContext.setLineWidth(2); graphOverlayContext.setFillStyle(CssColor.make(0,0,0)); AbsolutePanel absPanel = new AbsolutePanel(); absPanel.setWidth(w+"px"); absPanel.setHeight(h+"px"); absPanel.add(graph); absPanel.add(graphOverlay); absPanel.getElement().setId("graphPanel"); absPanel.setWidgetPosition(graph, 0, 0); absPanel.setWidgetPosition(graphOverlay, 0, 0); mainPanel.add(absPanel); graphOverlay.addMouseMoveHandler(new MouseMoveHandler() { public void onMouseMove(MouseMoveEvent ev) { mouseX = ev.getX(); mouseY = ev.getY(); if (mouseDown && selectedNode!=null) { graphOverlayContext.clearRect(0,0,w,h); //graphOverlayContext.fillText(mouseX+", "+mouseY, mouseX, mouseY); graphOverlayContext.setFillStyle(selectColor); graphOverlayContext.setStrokeStyle(selectColor); drawCircle(graphOverlayContext, selectedNode.getPosX(), selectedNode.getPosY(), radiusNode); drawCircle(graphOverlayContext, mouseX, mouseY, radiusNode); for (int[] pos : selectedTo) { drawArrow(graphOverlayContext, pos[0], pos[1], mouseX, mouseY,1); } for (int[] pos : selectedFrom) { drawArrow(graphOverlayContext, mouseX, mouseY, pos[0], pos[1],1); } drawNumberOutline(graphOverlayContext, selectedNode.getNo(), selectedNode.getPosX(), selectedNode.getPosY()); drawNumberOutline(graphOverlayContext, selectedNode.getNo(), mouseX, mouseY); } else { keyDetails.hide(); graphOverlayContext.clearRect(0,0,w,h); drawSelected(); KeyInfo ki = checkForKeyInfo(mouseX, mouseY); if (ki!=null) { graphOverlayContext.setFillStyle(selectColor); int x = ki.getPosX(); int y = ki.getPosY(); drawCircle(graphOverlayContext, x, y, radiusNode+2); drawNumberOutline(graphOverlayContext, ki.getNo(),x,y); if (cbShowDetails.getValue()) { int ox = 30; if (mouseX>500) ox = -500; int oy = 30; if (mouseY>350) oy = -350; int top = graphOverlay.getAbsoluteTop(); int left = graphOverlay.getAbsoluteLeft(); keyDetails.setPopupPosition(left+mouseX+ox, top+mouseY+oy); keyDetails.update(ki); keyDetails.show(); } } else { keyDetails.hide(); } } } }); graphOverlay.addMouseDownHandler(new MouseDownHandler() { public void onMouseDown(MouseDownEvent ev) { // mouseDownX = ev.getX(); // mouseDownY = ev.getY(); mouseDown = true; shiftDown = ev.isShiftKeyDown(); graphOverlayContext.clearRect(0,0,w,h); //graphOverlayContext.fillText(mouseX+", "+mouseY, mouseX, mouseY); selectedNode = checkForKeyInfo(mouseX, mouseY); if (selectedNode!=null) { selectConnections(selectedNode.getId()); if (shiftDown) { toggleSelected(selectedNode.getNo()); } } else { if (shiftDown) { //remove selection for (CheckBox cb : checks) { cb.setValue(false); } } } drawSelected(); } }); graphOverlay.addMouseUpHandler(new MouseUpHandler() { public void onMouseUp(MouseUpEvent ev) { // mouseUpX = ev.getX(); // mouseUpY = ev.getY(); if (mouseDown && selectedNode!=null) { graphOverlayContext.clearRect(0,0,w,h); selectedNode.setPosX(mouseX); selectedNode.setPosY(mouseY); drawGraph(); drawSelected(); } mouseDown = false; } }); } private void drawSelected() { //selected Vector<String> select = getSelectedKeyIds(); for (String sel : select) { KeyInfo ki = keyid_key.get(sel); if (ki!=null) { int x = ki.getPosX(); int y = ki.getPosY(); graphOverlayContext.setFillStyle(selectColor); graphOverlayContext.fillRect(x-radiusCircle, y-radiusCircle/2, 2*radiusCircle, radiusCircle); //drawCircle(graphOverlayContext, x, y, radiusCircle-3); drawNumberOutline(graphOverlayContext, ki.getNo(),x,y); } } } public void handleGetConnectionsToSelected(boolean in, boolean out) { Vector<String> selected = new Vector<String>(); Vector<KeyInfo> keys = user.getKeys(); for (int i=0; i<checks.size();i++) { if (checks.get(i).getValue()) { selected.add(keys.get(i).getId()); } } keyserverService.updateKeyInfoAndLogs(user.getName(), getCurrentNodeStates(), selected, in, out, new AsyncCallback<User>() { public void onFailure(Throwable caught) { } public void onSuccess(User result) { String msg = ""; DialogBox box = buildMessageBox("Result Message", msg); box.setPopupPosition(100, 100); box.show(); //updateUI(UI_USER); } }); } private Vector<NodeState> getCurrentNodeStates() { Vector<NodeState> states = new Vector<NodeState>(); Vector<KeyInfo> kilist = user.getKeys(); for (KeyInfo ki : kilist) { states.add(new NodeState(ki.getId(), ki.getPosX(), ki.getPosY(), ki.isIncomingLogs(), ki.isOutgoingLogs(), ki.isMyKey(), ki.isDirectTrust(), ki.getVisibilityLevel())); } return states; } private boolean isSelected(int keyNo) { return checks.get(keyNo-1).getValue().booleanValue(); } private void setSelected(int keyNo, boolean value) { checks.get(keyNo-1).setValue(value); } private void toggleSelected(int keyNo) { CheckBox cb = checks.get(keyNo-1); cb.setValue(!cb.getValue().booleanValue()); } private void drawNumberOutline(Context2d g, int no, double px, double py) { int ox = -2; if (no<10) { ox = -2; } else if (no<100) { ox = -6; } else { ox = -10; } g.setStrokeStyle(CssColor.make("white")); g.setFillStyle(CssColor.make("white")); g.strokeText(""+no, px+ox, py+3); g.setStrokeStyle(CssColor.make("black")); g.setFillStyle(CssColor.make("black")); g.fillText(""+no, px+ox, py+3); } public KeyInfo checkForKeyInfo(int x, int y) { Vector<KeyInfo> keys = user.getKeys(); for (KeyInfo ki : keys) { if (ki.getVisibilityLevel() >= visibilityLevel) { if (Math.abs(ki.getPosX()-x)<radiusNode && Math.abs(ki.getPosY()-y)<radiusNode) { return ki; } } } return null; } private void selectConnections(String keyid) { selectedFrom.removeAllElements(); selectedTo.removeAllElements(); Vector<KeyInfo> in = keyid_connect_in.get(keyid); if (in!=null) { for (KeyInfo ki : in) { selectedTo.add(new int[]{ki.getPosX(), ki.getPosY()}); } } Vector<KeyInfo> out = keyid_connect_out.get(keyid); if (out!=null) { for (KeyInfo ki : out) { selectedFrom.add(new int[]{ki.getPosX(), ki.getPosY()}); } } } public void layoutNodes() { if (user==null) return; Vector<KeyInfo> keys = user.getKeys(); if (keys.size()==0) return; Vector<KeyInfo> layouted = new Vector<KeyInfo>(); Vector<KeyInfo> notLayouted = new Vector<KeyInfo>(); for (KeyInfo ki : keys) { if (ki.getVisibilityLevel() >= visibilityLevel) { if (ki.getPosX()==-1 && ki.getPosY()==-1) { notLayouted.add(ki); } else { layouted.add(ki); } } } if (layouted.size()<2) { //circular int mX = w/2; int mY = h/2; int radius = (int)Math.rint(Math.min(mX,mY)*0.95); notLayouted.addAll(layouted); int nodecount = notLayouted.size(); double winkel = 2.0 * Math.PI / nodecount; for (int i = 0; i < nodecount; i++) { int x = (int)Math.rint(mX + Math.cos(i*winkel)*radius); int y = (int)Math.rint(mY + Math.sin(i*winkel)*radius); notLayouted.get(i).setPosX(x); notLayouted.get(i).setPosY(y); } } else { //random layout for (KeyInfo ki : notLayouted) { int x = (int)Math.rint(Math.random()*(w-40))+20; int y = (int)Math.rint(Math.random()*(h-40))+20; ki.setPosX(x); ki.setPosY(y); } } } public void drawGraph() { if (user==null || graph==null) return; Context2d g = graphContext; g.clearRect(0,0,w,h); g.setLineWidth(2); Vector<KeyInfo> keys = user.getKeys(); keyid_key.clear(); keyid_connect_in.clear(); keyid_connect_out.clear(); connect_count.clear(); if (keys.size()==0) return; for (KeyInfo ki : keys) { if (ki.getVisibilityLevel() >= visibilityLevel) { keyid_key.put(ki.getId(), ki); } } //draw arrows Vector<KeyConnection> connections = user.getConnections(); for (KeyConnection c : connections) { KeyInfo fromK = keyid_key.get(c.getFromId()); if (fromK != null) { KeyInfo toK = keyid_key.get(c.getToId()); if (toK != null) { String cname = fromK.getId()+toK.getId(); Integer cc = connect_count.get(cname); int cCount = 1; if (cc==null) { connect_count.put(cname, 1); } else { cCount = cc.intValue()+1; connect_count.put(cname, cCount); } Vector<KeyInfo> in = keyid_connect_in.get(toK.getId()); if (in == null) { in = new Vector<KeyInfo>(); keyid_connect_in.put(toK.getId(),in); } in.add(fromK); Vector<KeyInfo> out = keyid_connect_out.get(fromK.getId()); if (out == null) { out = new Vector<KeyInfo>(); keyid_connect_out.put(fromK.getId(),out); } out.add(toK); int x0 = fromK.getPosX(); int y0 = fromK.getPosY(); int x1 = toK.getPosX(); int y1 = toK.getPosY(); g.setStrokeStyle(edgeColors[c.getType()]); g.setFillStyle(edgeColors[c.getType()]); drawArrow(g, x0,y0,x1,y1, cCount); } } } //draw keys for (KeyInfo ki : keyid_key.values()) { int noCol = 0; if (ki.getLevel().equals("MASTER")) { noCol = 1; } else if (ki.getLevel().equals("SUB")) { noCol = 2; } else if (ki.getLevel().equals("REVOKE")) { noCol = 3; } //valid color -> border if (ki.isValid()) { g.setFillStyle(validColor); } else{ g.setFillStyle(notValidColor); } drawCircle(g, ki.getPosX(), ki.getPosY(), radiusCircle+3); //level color -> fill g.setFillStyle(nodeColors[noCol]); drawCircle(g, ki.getPosX(), ki.getPosY(), radiusCircle); g.setStrokeStyle(nodeTextColor[1]); g.setFillStyle(nodeTextColor[0]); int no = ki.getNo(); int ox = -2; if (no<10) { ox = -2; } else if (no<100) { ox = -6; } else { ox = -10; } g.fillText(""+no, ki.getPosX()+ox, ki.getPosY()+3); } } private void drawCircle(Context2d g, double x, double y, double radius) { g.beginPath(); g.arc(x, y, radius, 0, fullCircle); g.closePath(); g.fill(); } private void drawArrow(Context2d g, double x0, int y0, int x1, int y1, int cCount) { if (x0==x1 && y0 == y1) { //self reference double theta0 = Math.toRadians(0+(cCount-1)*20); double theta1 = Math.toRadians(90+(cCount-1)*20); double fromX = x0 + radiusNode * Math.cos(theta0); double fromY = y0 + radiusNode * Math.sin(theta0); double toX = x1 - radiusNode * Math.cos(theta1); double toY = y1 - radiusNode * Math.sin(theta1); drawCircle(g, fromX, fromY, 4); drawCircle(g, toX, toY, 4); g.beginPath(); g.moveTo(fromX, fromY); double cp1x = fromX+50; double cp1y = fromY-50; double cp2x = toX; double cp2y = toY; g.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, toX, toY); g.stroke(); g.beginPath(); g.moveTo(toX, toY); double theta2 = Math.toRadians(148); double x, y; x = toX - barb * Math.cos(theta2+phi); y = toY - barb * Math.sin(theta2+phi); //drawLine(g, toX, toY, x, y); g.lineTo(x, y); x = toX - barb * Math.cos(theta2-phi); y = toY - barb * Math.sin(theta2-phi); //drawLine(g, toX, toY, x, y); g.lineTo(x, y); g.closePath(); g.fill(); return; } double theta = Math.atan2(y1 - y0, x1 - x0); double fromX = x0 + radiusNode * Math.cos(theta); double fromY = y0 + radiusNode * Math.sin(theta); double toX = x1 - radiusNode * Math.cos(theta); double toY = y1 - radiusNode * Math.sin(theta); drawCircle(g, fromX, fromY, 4); drawCircle(g, toX, toY, 4); //drawLine(g, fromX, fromY, toX, toY); double cBarb = (0.25+0.05*cCount)*Math.sqrt((fromX-toX)*(fromX-toX)+(fromY-toY)*(fromY-toY)); double cPhi = Math.toRadians(11+(4*cCount)); g.beginPath(); g.moveTo(fromX, fromY); double cp1x = fromX - cBarb * Math.cos(theta+deg180-cPhi); double cp1y = fromY - cBarb * Math.sin(theta+deg180-cPhi); double cp2x = toX - cBarb * Math.cos(theta+cPhi); double cp2y = toY - cBarb * Math.sin(theta+cPhi); g.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, toX, toY); g.stroke(); double theta2 = Math.atan2(toY - cp2y, toX - cp2x); g.beginPath(); g.moveTo(toX, toY); double x, y; x = toX - barb * Math.cos(theta2+phi); y = toY - barb * Math.sin(theta2+phi); //drawLine(g, toX, toY, x, y); g.lineTo(x, y); x = toX - barb * Math.cos(theta2-phi); y = toY - barb * Math.sin(theta2-phi); //drawLine(g, toX, toY, x, y); g.lineTo(x, y); g.closePath(); g.fill(); } // private void drawLine(Context2d g, double x0, double y0, double x1, double y1) { // g.beginPath(); // g.moveTo(x0, y0); // g.lineTo(x1, y1); // g.closePath(); // g.stroke(); // } // // private void drawCurve(Context2d g, double x0, double y0, double x1, double y1, double phi, double barb) { // g.beginPath(); // g.moveTo(x0, y0); // // double theta = Math.atan2(y1 - y0, x1 - x0); // // double x, y; // double cp1x = x0 - barb * Math.cos(theta+phi); // double cp1y = y0 - barb * Math.sin(theta+phi); // // double cp2x = x1 - barb * Math.cos(theta+phi); // double cp2y = y1 - barb * Math.sin(theta+phi); // // g.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x1, y1); // g.stroke(); // } }